#pragma once
#include <iostream>
#include <cerrno>
#include <unistd.h>
#include <string>
#include <strings.h>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstdio>
#include <vector>
#include "Log.hpp"
#include "Comm.hpp"
#include "sockaddr.hpp"
#include "threadpool.hpp"
<<<<<<< HEAD
#define BUFFERSIZE 1024

// 将client信息与其发送的信息绑定
class Client_With_Message
{
public:
    Client_With_Message()
    {
    }
    Client_With_Message(int sockfd, Sockaddr owner, std::string message)
        : _sockfd(sockfd), _owner(owner), _message(message)
    {
    }
    const Client_With_Message &operator=(Client_With_Message &cwm)
    {
        if (this != &cwm)
        {
            _sockfd = cwm._sockfd;
            _owner = cwm._owner;
            _message = cwm._message;
        }
        return *this;
    }
    void Run()
    {
        // send back ： [ip: port]# message
        std::string send_back_message = "[";
        send_back_message += _owner.ip();
        send_back_message += ": ";
        send_back_message += std::to_string(_owner.port());
        send_back_message += "]# ";
        send_back_message += _message;

        ssize_t ret = sendto(_sockfd, send_back_message.c_str(), send_back_message.size(), 0, (const sockaddr *)&(_owner.addr_in()), _owner.len());
        if (ret == -1)
        {
            _log.LogMessage(ClassFile, Error, "server sendto error , errno : %d, strerror : %s\n", errno, strerror(errno));
            exit(SendtoError);
        }
    }

private:
    int _sockfd;
    Sockaddr _owner;      // 信息所持者
    std::string _message; // 信息
};

=======
#include "ProtectResourse.hpp"
#define BUFFERSIZE 1024

>>>>>>> refs/remotes/origin/master
class UDP_server
{
public:
    UDP_server(in_port_t server_port)
<<<<<<< HEAD
        : _sockfd(-1), _sk(server_port)
    {
=======
        : _sockfd(-1)
        , _sk(server_port)
    {
        pthread_mutex_init(&_mutex, nullptr);
>>>>>>> refs/remotes/origin/master
    }

    ~UDP_server()
    {
<<<<<<< HEAD
    }

    // 初始化server端
=======
        pthread_mutex_destroy(&_mutex);
    }

    // 初始化server端[ok]
>>>>>>> refs/remotes/origin/master
    void Init()
    {
        // 创建socket
        _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
        if (_sockfd == -1)
        {
            _log.LogMessage(ClassFile, Error, "server socket error -> errno : %d strerror : %s\n", errno, strerror(errno));
            exit(SocketError);
        }
        _log.LogMessage(ClassFile, Info, "server socket success, _fd = %d\n", _sockfd);

        // 绑定网络信息
        int ret = ::bind(_sockfd, (const struct sockaddr *)(&_sk.addr_in()), _sk.len());
        if (ret == -1)
        {
            _log.LogMessage(ClassFile, Error, "server bind error -> errno : %d strerror : %s\n", errno, strerror(errno));
            exit(BindError);
        }
<<<<<<< HEAD
    }
=======
    } //至此服务器的初始化已经完成，可以进行接收信息【OK】
>>>>>>> refs/remotes/origin/master

    // 接收网络信息
    void Start()
    {
<<<<<<< HEAD
        char buffer[BUFFERSIZE];
        // 服务端一直运行，不退出，除非我们人为退出
        while (true)
        {
            // 1. 获取【信息与发送者】
            struct sockaddr_in client;
            socklen_t client_len = sizeof(client);
            ssize_t ret = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)(&client), &client_len);

            // 2. 整合client信息与缓冲区内容，实际上是将信息与发送者进行绑定
            Client_With_Message cwm(_sockfd, client, buffer);

            // 获取完成，创建线程

            // 3. 主线程：将Client_With_Message加入到共享资源
            ThreadPool<Client_With_Message>::GetOnce()->Push(cwm);
            // 4. 让线程池的线程执行任务：将Client_With_Message发送给每一个Client
            ThreadPool<Client_With_Message>::GetOnce()->Start_Task();
        }

        // 5. 主线程回收线程池
        ThreadPool<Client_With_Message>::GetOnce()->Join_Threads();
=======
        //buffer的作用：将从client接收的信息，放入buffer中，再将buffer的信息给各个client中！
        char buffer[BUFFERSIZE];

        while (true)
        {
            // 1. 获取【message信息】【发送者client信息】

            struct sockaddr_in client; 
            socklen_t client_len = sizeof(client);
            ssize_t ret = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)(&client), &client_len); 
            buffer[ret] = 0;
            Sockaddr tmp(client);
            // 2. 添加在线用户，为的是后续将获取到的信息，发送给所有在线用户
            AddOnline(tmp);

            // 3. 获取完成，创建线程
            // 4. 主线程：将缓冲区信息加入到共享资源
            std::string rec; //[ip : port]# message
            rec += "[";
            rec += tmp.ip();
            rec += " : ";
            rec += std::to_string(tmp.port());
            rec += "]# ";
            rec += buffer;
            
            ThreadPool<std::string>::GetOnce(3, std::bind(&UDP_server::Send, this, std::placeholders::_1))->Push(rec);
            
            // 5. 让线程池的线程执行任务：将buffer发送给每一个Client
            ThreadPool<std::string>::GetOnce(3, std::bind(&UDP_server::Send, this, std::placeholders::_1))->Start_Task();
        }

        // 6. 主线程回收线程池
        ThreadPool<std::string>::GetOnce(3, std::bind(&UDP_server::Send, this, std::placeholders::_1))->Join_Threads();
    }

    //增加在线用户[ok]
    void AddOnline(const Sockaddr &online)
    {
        ProtectResourse protect(&_mutex);
        {
            for (auto &e : _online)
                if (e == online)
                    return;

            _online.push_back(online);
        }
    }

    //server的发送线程执行函数
    void Send(const std::string& buffer)
    {
        ProtectResourse protect(&_mutex);
        {
            for (auto &e : _online)
            {
                int ret = sendto(_sockfd, buffer.c_str(), buffer.size(), 0, (const sockaddr *)(&e.addr_in()), e.len());
                if (ret == -1)
                {
                    _log.LogMessage(ClassFile, Error, "server send error -> errno : %d strerror : %s\n", errno, strerror(errno));
                    exit(SendtoError);
                }
            }
        }
>>>>>>> refs/remotes/origin/master
    }

private:
    int _sockfd;
    Sockaddr _sk; // 初始化sockaddr
<<<<<<< HEAD
};
=======
    std::vector<Sockaddr> _online; // 当前在线用户[client端]，目的是可以让server给所有client发送信息
    pthread_mutex_t _mutex;
};

>>>>>>> refs/remotes/origin/master
